package com.elysia.blogspringboot.untils;

import com.aliyun.oss.*;
import com.aliyun.oss.model.*;
import com.elysia.blogspringboot.context.BaseContext;
import com.elysia.blogspringboot.enumeration.UploadFileEnum;
import com.elysia.blogspringboot.properties.AliyunOssProperties;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import java.io.*;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.UUID;

@Component
@AllArgsConstructor
@Slf4j
public class AliyunOssUtils {
    private final OSS ossClient;
    private final AliyunOssProperties aliyunOssProperties;
    /**
     * 文件上传，返回url
     * @param bytes 对象数据
     * @param objectName 对象名
     */
    public String upload(byte[] bytes, String objectName, UploadFileEnum uploadFileEnum) {
        // 1. 校验参数
        if (bytes == null || bytes.length == 0)
            throw new IllegalArgumentException("上传文件不能为空");
        if (!isObjectNameValid(objectName))
            throw new IllegalArgumentException("非法的OSS对象名：" + objectName);
        try {
            String fileName = String.format("%s/%s",
                    uploadFileEnum.getValue(),
                    objectName);
            // 2. 先上传文件（无shutdown，复用单例Client）
            PutObjectRequest request = new PutObjectRequest(
                    aliyunOssProperties.getBucketName(),
                    fileName,
                    new ByteArrayInputStream(bytes)
            );
            ossClient.putObject(request); // 先完成上传
            // 3. 上传成功后，再为头像文件设置公共读权限（关键修正）
            if (uploadFileEnum == UploadFileEnum.AVATAR) {
                ossClient.setObjectAcl(
                        aliyunOssProperties.getBucketName(),
                        fileName,
                        CannedAccessControlList.PublicRead
                );
            }
            // 4. 构建访问URL（兼容OSS访问规则）
            String accessUrl = String.format("https://%s.%s/%s",
                    aliyunOssProperties.getBucketName(),
                    aliyunOssProperties.getEndpoint(),
                    URLEncoder.encode(fileName, StandardCharsets.UTF_8));
            log.info("文件上传成功，访问URL：{}", accessUrl);
            return accessUrl;
        } catch (OSSException oe) {
            log.error("OSS服务异常：code={}, msg={}", oe.getErrorCode(), oe.getErrorMessage(), oe);
            throw new RuntimeException("文件上传失败（OSS服务错误）", oe);
        } catch (ClientException ce) {
            log.error("客户端异常：msg={}", ce.getMessage(), ce);
            throw new RuntimeException("文件上传失败（客户端错误）", ce);
        }
    }

    /**
     * 生成OSS临时下载URL
     * @param objectName OSS中的文件路径（如"document/12345.png"）
     * @param expireSeconds 有效期（秒），建议不超过3600（1小时）
     * @return 带签名的临时URL
     */
    public String generateTempDownloadUrl(String objectName, int expireSeconds, UploadFileEnum uploadFileEnum, String fileName) {
        // 1. 参数校验（保持不变）
        if (expireSeconds <= 0 || expireSeconds > 86400)
            throw new IllegalArgumentException("有效期必须在1-86400秒之间");
        if (!isObjectNameValid(objectName))
            throw new IllegalArgumentException("非法的文件路径：" + objectName);

        String bucketName = aliyunOssProperties.getBucketName();
        try {
            // 2. 动态拼接完整文件路径（与upload方法逻辑一致）
            String dir = uploadFileEnum.getValue();
            String fullObjectName = String.format("%s/%s", dir, objectName);
            log.info("完整文件路径：{}", fullObjectName);

            // 3. 检查文件是否存在
            if (!ossClient.doesObjectExist(bucketName, fullObjectName)) {
                log.warn("文件不存在：{}", fullObjectName);
                throw new RuntimeException("文件不存在");
            }

            // 4. 配置响应头（解决文件名问题）
            // 4.1 编码下载文件名（避免中文乱码）
            String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8);
            // 4.2 创建响应头覆盖对象，设置Content-Disposition（强制指定下载文件名）
            ResponseHeaderOverrides responseHeaders = new ResponseHeaderOverrides();
            responseHeaders.setContentDisposition("attachment; filename*=UTF-8''" + encodedFileName);

            // 5. 构建GeneratePresignedUrlRequest（关键修正：用setXxx方法替代链式调用）
            GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(
                    bucketName,          // Bucket名
                    fullObjectName,      // OSS完整文件路径
                    HttpMethod.GET       // 下载用GET方法
            );
            // 5.1 设置过期时间（用setExpiration，而非withExpiration）
            request.setExpiration(new Date(System.currentTimeMillis() + expireSeconds * 1000L));
            // 5.2 设置响应头（用setResponseHeaders，兼容所有3.x版本）
            request.setResponseHeaders(responseHeaders);

            // 6. 生成HTTPS临时URL（依赖全局OSSClient的HTTPS配置）
            URL signedUrl = ossClient.generatePresignedUrl(request);
            String tempUrl = signedUrl.toString();
            log.info("用户{}生成临时下载URL，路径：{}，有效期：{}秒",
                    BaseContext.getCurrentId(), fullObjectName, expireSeconds);
            return tempUrl;
        } catch (Exception e) {
            log.error("生成临时下载URL失败", e);
            throw new RuntimeException("获取下载链接失败：" + e.getMessage());
        }
    }
    // 安全检查：验证文件名是否合法
    private boolean isObjectNameValid(String objectName) {
        // 1. 非空且长度<=1023字节
        if (objectName == null || objectName.isEmpty() || objectName.getBytes(StandardCharsets.UTF_8).length > 1023) {
            return false;
        }
        // 2. 禁止路径遍历（包括解码后的..）
        if (objectName.contains("..") || objectName.startsWith("/") || objectName.startsWith("\\")) {
            return false;
        }
        // 3. 禁止OSS非法字符（? * : " < > | \ / 等）
        String illegalChars = "[?*:\"<>|\\\\/]";
        return !objectName.matches(".*" + illegalChars + ".*");
    }

    /**
     * 获取文件名（从对象名中截取，如"blog/test.jpg" -> "test.jpg"）
     */
    private String getFileName(String objectName) {
        int lastIndex = objectName.lastIndexOf("/");
        return lastIndex != -1 ? objectName.substring(lastIndex + 1) : objectName;
    }
}
